package com.sojson.util.verifycode.impl;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

import javax.imageio.ImageIO;

import com.sojson.constant.Constant;
import com.sojson.util.StringUtil;
import com.sojson.util.cache.CacheUtil;
import com.sojson.util.file.FileUtil;
import com.sojson.util.verifycode.VerifyCodeUtil;
import com.sojson.util.verifycode.VerifyCodeUtil.Verify;
import com.sojson.util.verifycode.VerifyCodeUtilInterface;

/**
 * 验证码工具类简单实现类
 * 
 * @author liu
 * @Date 2020-06-02 15:11:28
 * @Description
 *
 */
public class VerifyCodeUtilSimpleImpl implements VerifyCodeUtilInterface {

    private static VerifyCodeUtilSimpleImpl verifyCodeUtilImpl;
    // =================================================================================================
    // ===========================================验证码样式===============================================
    // =================================================================================================
    /** 验证码的默认长度 */
    public final int verifySize = 4;
    /** 验证码默认有效时间(单位: 毫秒) */
    public final int defaultTime = 1000 * 60;
    /** 图片的宽度 */
    public final int width = 140;
    /** 图片的高度 */
    public final int height = 40;
    /** 字体大小 */
    public final int fontSize = 38;
    /** 字体所占宽度(不是字体宽度) */
    public final int fontWidth = 30;
    /** 字体底部与边框顶部的距离 */
    public final int heightMargin = 35;
    /** 验证码干扰距离 */
    public final int lineHeight = 100;

    private VerifyCodeUtilSimpleImpl() {}

    /**
     * 使用系统默认字符源生成默认长度的验证码
     * 
     * @return
     */
    @Override
    public Verify generateVerifyCode() {
        return generateVerifyCode(verifySize, VerifyCodeUtil.VERIFY_CODES, VerifyCodeUtil.WORD_NUMBER);
    }

    /**
     * 使用系统默认字符源生成验证码
     * 
     * @param verifySize 验证码长度
     * @return
     */
    @Override
    public Verify generateVerifyCode(int verifySize) {
        return generateVerifyCode(verifySize, VerifyCodeUtil.VERIFY_CODES, VerifyCodeUtil.RANDOM_TYPE);
    }

    /**
     * 使用指定源生成默认长度的验证码
     * 
     * @param sources 验证码字符源
     * @return
     */
    @Override
    public Verify generateVerifyCode(char[] sources) {
        return generateVerifyCode(verifySize, sources, VerifyCodeUtil.RANDOM_TYPE);
    }

    /**
     * 使用系统默认字符源生成验证码
     * 
     * @param minSize 验证码最小长度
     * @param maxSize 验证码最大长度
     * @return
     */
    @Override
    public Verify generateVerifyCode(int minSize, int maxSize) {
        return generateVerifyCode(minSize + Constant.RANDOM.nextInt(maxSize - minSize), VerifyCodeUtil.VERIFY_CODES,
            VerifyCodeUtil.RANDOM_TYPE);
    }

    /**
     * 使用指定源生成验证码
     * 
     * @param minSize 验证码最小长度
     * @param maxSize 验证码最大长度
     * @param sources 验证码字符源
     * @return
     */
    @Override
    public Verify generateVerifyCode(int minSize, int maxSize, char[] sources) {
        return generateVerifyCode(minSize + Constant.RANDOM.nextInt(maxSize - minSize), sources,
            VerifyCodeUtil.RANDOM_TYPE);
    }

    /**
     * 使用指定源生成验证码
     * 
     * @param verifySize 验证码长度
     * @param sources 验证码字符源
     * @return
     */
    @Override
    public Verify generateVerifyCode(int verifySize, char[] sources) {
        return generateVerifyCode(verifySize, sources, VerifyCodeUtil.RANDOM_TYPE);
    }

    /**
     * 使用指定源生成验证码
     * 
     * @param sources 验证码字符源
     * @param codeType 验证码类型
     * @return
     */
    @Override
    public Verify generateVerifyCode(char[] sources, int codeType) {
        return generateVerifyCode(verifySize, sources, codeType);
    }

    /**
     * 使用指定源生成验证码
     * 
     * @param verifySize 验证码长度
     * @param sources 验证码字符源
     * @param codeType 验证码类型
     * @return
     */
    @Override
    public Verify generateVerifyCode(int verifySize, char[] sources, int codeType) {
        if (isBlankObject(sources) || sources.length == 0) {
            return null;
        }
        // 如果类型为null就随机给一种类型
        if (codeType == -1) {
            codeType = Constant.RANDOM.nextInt(VerifyCodeUtil.TYPE_COUNT);
        }
        // 根据类型生成验证码
        switch (codeType) {
            case VerifyCodeUtil.WORD_NUMBER:
                return generateVerifyCodeByWordNumber(verifySize, sources);
            case VerifyCodeUtil.COMPUTE:
                return generateVerifyCodeByCompute();
            default:
        }
        return null;
    }

    /**
     * 生成单词数字验证码
     * 
     * @return
     */
    @Override
    public Verify generateVerifyCodeByWordNumber() {
        return generateVerifyCodeByWordNumber(verifySize, VerifyCodeUtil.VERIFY_CODES);
    }

    /**
     * 生成单词数字验证码
     * 
     * @param verifySize 验证码长度
     * @param sources 验证码字符源
     * @return
     */
    @Override
    public Verify generateVerifyCodeByWordNumber(int verifySize, char[] sources) {
        int codesLen = sources.length - 1;
        StringBuilder verifyCode = new StringBuilder(verifySize);
        // 根据长度生成验证码
        for (int i = 0; i < verifySize; i++) {
            verifyCode.append(sources[Constant.RANDOM.nextInt(codesLen)]);
        }
        // 封装
        String string = verifyCode.toString();
        Verify verify = new Verify();
        verify.setCode(string);
        verify.setValue(string);
        return verify;
    }

    /**
     * 生成算法验证码
     * 
     * @return
     */
    @Override
    public Verify generateVerifyCodeByCompute() {
        StringBuilder verifyCode = new StringBuilder(6);
        Random random = Constant.RANDOM;

        // 符号
        int symbolInt = random.nextInt(VerifyCodeUtil.SYMBOL.length);
        char symbol = VerifyCodeUtil.SYMBOL[symbolInt];

        // 第一个整数
        int nextIntOne = 0;
        // 第二个整数
        int nextIntTwo = 0;

        // 根据算法生成验证码并封装
        Verify verify = new Verify();
        switch (symbol) {
            case '+':
                nextIntOne = random.nextInt(100);
                nextIntTwo = random.nextInt(100 - nextIntOne);
                verify.setValue(String.valueOf(nextIntOne + nextIntTwo));
                verifyCode.append(nextIntOne);
                break;
            case '-':
                nextIntOne = random.nextInt(100);
                nextIntTwo = random.nextInt(100);
                verify.setValue(String.valueOf(nextIntOne - nextIntTwo));
                verifyCode.append(nextIntOne);
                break;
            case '×':
                nextIntTwo = random.nextInt(20);
                nextIntOne = random.nextInt((99 / (nextIntTwo + 1)) + 1);
                verify.setValue(String.valueOf(nextIntOne * nextIntTwo));
                verifyCode.append(nextIntOne);
                break;
            case '÷':
                nextIntTwo = random.nextInt(20);
                nextIntOne = random.nextInt((99 / (nextIntTwo + 1)) + 1);
                verify.setValue(String.valueOf(nextIntOne));
                verifyCode.append(nextIntOne * nextIntTwo);
                break;
            default:
        }
        verifyCode.append(symbol).append(nextIntTwo).append("=");
        verify.setCode(verifyCode.toString());
        return verify;
    }

    /**
     * 获取验证码
     * 
     * @param codePrefix 验证码名称
     * @throws IOException 
     */
    @Override
    public Verify getVerifyCode(String codePrefix) throws IOException {
        return get(packagingverifyCodeName(codePrefix));
    }

    /**
     * 清除验证码
     * 
     * @param codePrefix 验证码名称
     * @throws IOException 
     */
    @Override
    public void clearVerifyCode(String codePrefix) throws IOException {
        delByKey(packagingverifyCodeName(codePrefix));
    }

    /**
     * 清除验证码
     * 
     * @param verify 验证码
     * @throws IOException 
     */
    @Override
    public void clearVerifyCode(Verify verify) throws IOException {
        delByKey(verify.getCodeName());
    }

    /**
     * 对比验证码
     * 
     * @param codeName 验证码名称
     * @param code 验证码
     * @return
     * @throws IOException 
     */
    @Override
    public boolean verifyCode(String codeName, String code) throws IOException {
        Verify v = get(packagingverifyCodeName(codeName));
        return equalsIgnoreCase(isNotBlankObject(v) ? v.getValue() : null, code);
    }

    /**
     * 对比验证码
     * 
     * @param verify 验证码
     * @param code 验证码
     * @return
     * @throws IOException 
     */
    @Override
    public boolean verifyCode(Verify verify, String code) throws IOException {
        return equalsIgnoreCase(isNotBlankObject(verify) ? verify.getValue() : null, code);
    }

    /**
     * 设置验证码名称
     * 
     * @param verifyCodePrefix 验证码前缀
     * @return
     */
    @Override
    public String packagingverifyCodeName(String verifyCodePrefix) {
        return isNotBlankObject(verifyCodePrefix) ? verifyCodePrefix + VerifyCodeUtil.CODE_SUFFIX
            : VerifyCodeUtil.CODE_SUFFIX;
    }

    /**
     * 对比并清除验证码
     * 
     * @param codeName 验证码名称
     * @param code 验证码
     * @return
     * @throws IOException 
     */
    @Override
    public boolean verifyAndClearCode(String codeName, String code) throws IOException {
        boolean res = verifyCode(codeName, code);

        // 验证成功就清空验证码
        if (res) {
            clearVerifyCode(codeName);
        }
        return res;
    }

    /**
     * 对比并清除验证码
     * 
     * @param verify 验证码
     * @param code 验证码
     * @return
     * @throws IOException 
     */
    @Override
    public boolean verifyAndClearCode(Verify verify, String code) throws IOException {
        boolean res = verifyCode(verify, code);

        // 验证成功就清空验证码
        if (res) {
            clearVerifyCode(verify);
        }
        return res;
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public BufferedImage getVerifyCodeBufferedImage(String code) {
        return getVerifyCodeBufferedImage(width, height, fontSize, fontWidth, heightMargin, code, lineHeight);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public BufferedImage getVerifyCodeBufferedImage(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code) {
        return getVerifyCodeBufferedImage(width, height, fontSize, fontWidth, heightMargin, code, lineHeight);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public BufferedImage getVerifyCodeBufferedImage(String code, int lineHeight) {
        return getVerifyCodeBufferedImage(width, height, fontSize, fontWidth, heightMargin, code, lineHeight);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public BufferedImage getVerifyCodeBufferedImage(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code, int lineHeight) {
        Graphics2D g2 = null;

        Random random = Constant.RANDOM;
        // 获取验证码长度
        int verifySize = code.length();
        // 图片宽度减去所有字体后剩下的宽度/2
        int fontWidthYu = (width - fontWidth * (verifySize)) / 2;
        // 三基色
        int red = 0, green = 0, blue = 0;
        // 干扰线位置
        int xOne = 0, yOne = 0, xTwo = width, yTwo = 0;

        // 构造一个预定义dimage类型之一的BufferedImage。图像的颜色空间是默认的sRGB空间
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        try {
            // 创建一个Graphics2D，它可以用来绘制到这个BufferedImage。
            g2 = image.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            /** 以下是样式风格 */
            // 设置背景色
            g2.setColor(Color.WHITE);
            g2.fillRect(0, 0, width, height);

            // 绘制干扰线
            // 添加一些高度,为了让干扰线居中
            int yu = (height % lineHeight) / 2;
            for (int i = yu; i <= height; i++) {
                // 在xOne,yOne到xTwo,yTwo的位置生成一条线
                yOne = i;
                yTwo = i;
                // 设置线条的颜色
                red = random.nextInt(255);
                green = random.nextInt(255);
                blue = random.nextInt(255);
                g2.setColor(new Color(red, green, blue));
                g2.drawLine(xOne, yOne, xTwo, yTwo);
                i += lineHeight;
            }

            // 添加噪点,噪声率
            // float yawpRate = 0.05f;
            // int area = (int)(yawpRate * width * height);
            // for (int i = 0; i < area; i++) {
            // int yrX = random.nextInt(width);
            // int yrY = random.nextInt(height);
            // int rgb = getRandomIntColor();
            // image.setRGB(yrX, yrY, rgb);
            // }

            // 使图片扭曲
            shear(g2, width, height, Color.WHITE);

            // 设置字体样式
            Font font = new Font("Algerian", Font.ITALIC, fontSize);
            g2.setFont(font);

            // 添加第一个字
            // red = random.nextInt(255);
            // green = random.nextInt(255);
            // blue = random.nextInt(255);
            // g2.setColor(new Color(red, green, blue));
            // g2.drawString("{", 0 + fontWidthYu, heightMargin);

            // 添加验证码
            String[] split = code.split("");
            for (int i = 0; i < verifySize; i++) {
                // 产生随机的颜色值，让输出的每个字符的颜色值都将不同。
                red = random.nextInt(255);
                green = random.nextInt(255);
                blue = random.nextInt(255);
                g2.setColor(new Color(red, green, blue));
                g2.drawString(split[i], fontWidth * i + fontWidthYu, heightMargin);
                // 将产生的四个随机数组合在一起。
            }

            // 添加最后一个字
            // red = random.nextInt(255);
            // green = random.nextInt(255);
            // blue = random.nextInt(255);
            // g2.setColor(new Color(red, green, blue));
            // g2.drawString("}", fontWidth * (verifySize + 1) + fontWidthYu, heightMargin);

            image.flush();
        } finally {
            // 释放资源
            g2.dispose();
        }
        return image;
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(String code) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(String code, String suffix) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(int width, int height, int fontSize, int fontWidth, int heightMargin, String code)
        throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(int width, int height, int fontSize, int fontWidth, int heightMargin, String code,
        String suffix) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(String code, int lineHeight) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(String code, int lineHeight, String suffix) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(int width, int height, int fontSize, int fontWidth, int heightMargin, String code,
        int lineHeight) throws IOException {
        return getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public byte[] getVerifyCodeBytes(int width, int height, int fontSize, int fontWidth, int heightMargin, String code,
        int lineHeight, String suffix) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = null;

        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(
                getVerifyCodeBufferedImage(width, height, fontSize, fontWidth, heightMargin, code, lineHeight), suffix,
                byteArrayOutputStream);
        } finally {
            closeStream(byteArrayOutputStream);
        }

        return byteArrayOutputStream.toByteArray();
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(String code) throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(String code, String suffix) throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code) throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code, String suffix) throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(String code, int lineHeight) throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code, lineHeight,
            Constant.GENERATE_IMG_SUFFIX);
    }

    /**
     * 生成验证码图片
     * 
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(String code, int lineHeight, String suffix)
        throws IOException {
        return getVerifyCodeInputStream(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix);
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code, int lineHeight) throws IOException {
        return new ByteArrayInputStream(
            getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight));
    }

    /**
     * 生成验证码图片
     * 
     * @param width 图片宽度
     * @param height 图片高度
     * @param fontSize 字体大小
     * @param fontWidth 字体所占宽度(不是字体宽度)
     * @param heightMargin 字体底部与底部边框的距离
     * @param code 验证码
     * @param lineHeight 验证码干扰线距离
     * @param suffix 验证码图片的格式
     * @throws IOException
     */
    @Override
    public ByteArrayInputStream getVerifyCodeInputStream(int width, int height, int fontSize, int fontWidth,
        int heightMargin, String code, int lineHeight, String suffix) throws IOException {
        return new ByteArrayInputStream(
            getVerifyCodeBytes(width, height, fontSize, fontWidth, heightMargin, code, lineHeight, suffix));
    }

    @SuppressWarnings("unused")
    private int getRandomIntColor() {
        int[] rgb = getRandomRgb();
        int color = 0;
        for (int c : rgb) {
            color = color << 8;
            color = color | c;
        }
        return color;
    }

    private int[] getRandomRgb() {
        int length = 3;
        int[] rgb = new int[length];
        for (int i = 0; i < length; i++) {
            rgb[i] = Constant.RANDOM.nextInt(255);
        }
        return rgb;
    }

    private void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private void shearX(Graphics g, int w1, int h1, Color color) {

        int period = Constant.RANDOM.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = Constant.RANDOM.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (period >> 1) * Math.sin((double)i / (double)period + (6.2831853071795862D * phase) / frames);
            g.copyArea(0, i, w1, 1, (int)d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int)d, i, 0, i);
                g.drawLine((int)d + w1, i, w1, i);
            }
        }

    }

    private void shearY(Graphics g, int w1, int h1, Color color) {

        // 50
        int period = Constant.RANDOM.nextInt(40) + 10;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (period >> 1) * Math.sin((double)i / (double)period + (6.2831853071795862D * phase) / frames);
            g.copyArea(i, 0, 1, h1, 0, (int)d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int)d, i, 0);
                g.drawLine(i, (int)d + h1, i, h1);
            }

        }

    }

    // public void main(String[] args) throws IOException {
    // File dir = new File("D:/verifies");
    // for (int i = 0; i < 10; i++) {
    // Verify generateVerifyCode = generateVerifyCode();
    // String verifyCode = generateVerifyCode.getCode();
    // System.out.println(generateVerifyCode.getCreateTime().getTime());
    // File file = new File(dir, RandomGUIDUtils.generateGUID() + ".jpg");
    // BufferedImage outputImage = outputImage(verifyCode);
    // ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    // ImageIO.write(outputImage, "jpg", byteArrayOutputStream);
    // byte[] byteArray = byteArrayOutputStream.toByteArray();
    // new FileOutputStream(file).write(byteArray);
    // }
    // }

    /**
     * 判断一个对象是否是空(空对象)
     * 
     * @param obj
     * @return
     */
    private static boolean isBlankObject(Object obj) {
        return StringUtil.isBlankObject(obj);
    }

    /**
     * 判断一个对象是否不是空(空对象)
     * 
     * @param obj
     * @return
     */
    private static boolean isNotBlankObject(Object obj) {
        return StringUtil.isNotBlankObject(obj);
    }

    private static boolean equalsIgnoreCase(String str1, String str2) {
        return StringUtil.equalsIgnoreCase(str1, str2);
    }

    /**
     * 从缓存中获取值
     * 
     * @param <T>
     * @param key
     * @return
     * @throws IOException
     */
    public static <T> T get(String key) throws IOException {
        return CacheUtil.get(key);
    }

    /**
     * 删除某个数据
     * 
     * @param dkey
     * @return
     * @throws IOException
     */
    public static Long delByKey(String dkey) throws IOException {
        return CacheUtil.delByKey(dkey);
    }

    /**
     * 关闭字节输出流
     * 
     * @param outputStream 需要关闭的字节输出流
     * @throws IOException
     */
    public static void closeStream(OutputStream outputStream) throws IOException {
        FileUtil.closeStream(outputStream);
    }

    /** 获取验证码的默认长度 */
    @Override
    public int getVerifySize() {
        return verifySize;
    }

    /** 获取验证码默认有效时间(单位: 毫秒) */
    @Override
    public int getDefaultTime() {
        return defaultTime;
    }

    /** 获取图片的宽度 */
    @Override
    public int getWidth() {
        return width;
    }

    /** 获取图片的高度 */
    @Override
    public int getHeight() {
        return height;
    }

    /** 获取字体大小 */
    @Override
    public int getFontSize() {
        return fontSize;
    }

    /** 获取字体所占宽度(不是字体宽度) */
    @Override
    public int getFontWidth() {
        return fontWidth;
    }

    /** 获取字体底部与边框顶部的距离 */
    @Override
    public int getHeightMargin() {
        return heightMargin;
    }

    /** 获取验证码干扰距离 */
    @Override
    public int getLineHeight() {
        return lineHeight;
    }

    public static VerifyCodeUtilSimpleImpl getInstances() {
        if (isBlankObject(verifyCodeUtilImpl)) {
            verifyCodeUtilImpl = new VerifyCodeUtilSimpleImpl();
        }
        return verifyCodeUtilImpl;
    }

}